#! /usr/bin/python
import sys
import os
import re
import backupManager.config as config
from backupManager.enum import AEnums
import math
import datetime
import Pyro4
import hashlib
import traceback


host = ''
port = config.pyro_port


class indexServer(object):
    def __init__(self):
        self.indexer = Indexer()

    def index(self, commDict):
        self.indexer.count = 0
        return self.indexer.indexDrive(commDict)

    def count(self):
        return self.indexer.count


class Indexer(object):
    def __init__(self):
        self.count = 0

    def compileValidExt(self, validext):
        if validext:
            validext = '|'.join(validext)
            validext = "\.(" + validext + ")$"
            validext = re.compile(str(validext), re.IGNORECASE)
        else:
            validext = None
        return validext

    def compileSkipList(self, skip):
        skip = '|'.join(skip)
        if skip:
            skip = re.compile(str(skip), re.IGNORECASE)
        else:
            skip = None
        return skip


    def indexDrive(self, commDict):
        print 'Received index request', str(datetime.datetime.now()), commDict
        if not os.path.exists(commDict[AEnums.REMOTEPATH]):
            return AEnums.NOTEXIST
        validext = self.compileValidExt(commDict[AEnums.VALIDEXTS])
        skip = self.compileSkipList(commDict[AEnums.SKIPLIST])
        root = []
        files = []
        # noinspection PyBroadException
        try:
            for path in commDict[AEnums.PATHS]:
                path = path.strip(commDict[AEnums.SEP])
                path = path.replace(commDict[AEnums.SEP], os.sep)
                path = unicode(path)
                newFile = self.index(path, validext, skip, files, root=True, commDict=commDict)
                files.append(newFile)
                root.append(newFile[AEnums.ID])
        except:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            traceback.print_exception(exc_type, exc_value, exc_traceback, limit=2, file=sys.stdout)
        print 'Done indexing!', str(datetime.datetime.now())
        return {
            AEnums.FILES: files,
            AEnums.ROOT: root,
        }

    def createDirRef(self, path, dirName, root, commDict):
        fd = {
            AEnums.NAME: os.path.basename(dirName),
            AEnums.PATH: os.path.dirname(os.path.join(commDict[AEnums.REMOTEPATH], path)).replace(
                commDict[AEnums.REMOTEPATH], '').replace(
                    os.sep, commDict[AEnums.SEP]),
            AEnums.PREFIX: commDict[AEnums.NAME] + ':',
            AEnums.DIR: True,
            AEnums.VOLNAME: commDict[AEnums.NAME],
            AEnums.SERIAL: commDict[AEnums.SERIAL],
            AEnums.SIZE: 0,
            AEnums.MTIME: 0,
            AEnums.PARENT: '',
            AEnums.SETNAME: commDict[AEnums.SETNAME],
        }
        m = hashlib.md5()
        m.update(fd[AEnums.NAME].encode('UTF-16'))
        m.update(fd[AEnums.PATH].encode('UTF-16'))
        m.update(fd[AEnums.VOLNAME])
        m.update(fd[AEnums.SERIAL])
        m.update(fd[AEnums.SETNAME])
        if root:
            m.update(AEnums.ROOT)
        fileID = m.hexdigest()
        fd[AEnums.ID] = fileID
        return fd

    def createFileRef(self, path, dirName, foundFile, commDict):
        mtime = int(math.floor(os.path.getmtime(os.path.join(dirName, foundFile))))
        if mtime % 2:
            mtime += 1
        newFile = {
            AEnums.NAME: foundFile,
            AEnums.PATH: path.replace(os.sep, commDict[AEnums.SEP]),
            AEnums.PREFIX: commDict[AEnums.NAME] + ':',
            AEnums.DIR: False,
            AEnums.SIZE: float(os.path.getsize(os.path.join(dirName, foundFile))) / float(1024),
            AEnums.MTIME: mtime,
            AEnums.VOLNAME: commDict[AEnums.NAME],
            AEnums.SERIAL: commDict[AEnums.SERIAL],
            AEnums.SETNAME: commDict[AEnums.SETNAME],
        }
        m = hashlib.md5()
        m.update(newFile[AEnums.NAME].encode('UTF-16'))
        m.update(newFile[AEnums.PATH].encode('UTF-16'))
        m.update(newFile[AEnums.VOLNAME])
        m.update(newFile[AEnums.SERIAL])
        m.update(newFile[AEnums.SETNAME])
        fileID = m.hexdigest()
        newFile[AEnums.ID] = fileID
        return newFile

    def shouldProcess(self, dirName, foundFile, skip, ext):
        if not os.access(os.path.join(dirName, foundFile), os.R_OK):
            return False
        if skip:
            if re.search(skip, foundFile):
                return False
        if not os.path.isdir(os.path.join(dirName, foundFile)):
            if ext:
                if not re.search(ext, foundFile):
                    return False
        return True


    def getIndexFiles(self, dirName):
        # noinspection PyBroadException
        try:
            files = os.listdir(unicode(dirName))
        except:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            traceback.print_exception(exc_type, exc_value, exc_traceback, limit=2, file=sys.stdout)
            return None
        return files

    def index(self, path, validext, skip, filesIn, root=False, commDict=None):
        """

        :param path: directory path to index
        :param validext: list of valid extensions to look for
        :param skip: list of regular expressions to skip
        :param filesIn: list object to append new files to
        :param root: is this a root directory?
        :param commDict: a dictionary filled with path information (i.e. local -> remote mapping)
        :return: a file descriptor representing the directory
        """
        self.count = len(filesIn)
        dirName = os.path.join(commDict[AEnums.REMOTEPATH], path)
        while re.search(r'/$', dirName, re.IGNORECASE):
            dirName = re.sub(r'/$', '', dirName)
        if not os.path.isdir(dirName):
            return None
        files = self.getIndexFiles(dirName)
        fd = self.createDirRef(path, dirName, root, commDict)
        passedFiles = []
        for foundFile in files[:]:
            if not self.shouldProcess(dirName, foundFile, skip, validext):
                continue
            if os.path.isdir(os.path.join(dirName, foundFile)):
                newFile = self.index(os.path.join(path, foundFile), validext, skip, filesIn, False, commDict)
            else:
                newFile = self.createFileRef(path, dirName, foundFile, commDict)
            if newFile:
                newFile[AEnums.PARENT] = fd[AEnums.ID]
                passedFiles.append(newFile)
        if not passedFiles and not root:
            return None
        [filesIn.append(x) for x in passedFiles]
        return fd


def main():
    i = indexServer()
    Pyro4.Daemon.serveSimple(
        {
            i: "anybackup.indexserver"
        },
        ns=False, port=port, host='')

if __name__ == "__main__":
    main()
